import sys
import os
import re
import markdown
from PyQt5.QtCore import Qt, QTimer, QUrl, QRegExp
from PyQt5.QtWidgets import (
    QApplication, QMainWindow, QWidget, QSplitter,
    QPlainTextEdit, QVBoxLayout, QTreeWidget, QTreeWidgetItem,
    QHBoxLayout, QLabel,QFileDialog,QMessageBox,QProgressDialog
)
from PyQt5.QtWebEngineWidgets import QWebEngineView
from PyQt5.QtGui import QTextCursor
from mod_math import MathFormat
from PyQt5.QtPrintSupport import QPrinter
from mod_keyboard import LatexKeyboard




# ==============================
# 主窗口类
# ==============================
class MarkdownEditor(QMainWindow):
    def __init__(self):
        super().__init__()
        self.init_ui()
        self.init_content()
        self.last_known_line_count = 0
        self.current_file = "Untitled.md"


    def init_ui(self):
        """界面初始化"""
        self.setWindowTitle("Markdown Editor with Outline")
        self.setGeometry(100, 100, 1600, 900)
        self.baseurl = QUrl.fromLocalFile(os.path.dirname(os.path.abspath(__file__)) + '/')


        # 主容器
        central_widget = QWidget()
        self.setCentralWidget(central_widget)
        main_layout = QHBoxLayout(central_widget)
        main_layout.setContentsMargins(0, 0, 0, 0)

        # 三栏分割布局
        self.splitter = QSplitter(Qt.Horizontal)
        main_layout.addWidget(self.splitter)

        # 左侧大纲面板
        self.outline_widget = QWidget()
        outline_layout = QVBoxLayout(self.outline_widget)
        outline_layout.setContentsMargins(5, 5, 5, 5)
        
        self.outline_label = QLabel("文档大纲")
        self.outline_tree = QTreeWidget()
        self.outline_tree.setHeaderHidden(True)
        self.outline_tree.itemClicked.connect(self.jump_to_heading)
        
        outline_layout.addWidget(self.outline_label)
        outline_layout.addWidget(self.outline_tree)
        self.splitter.addWidget(self.outline_widget)

        # 中间编辑区域
        editor_widget = QWidget()
        editor_layout = QVBoxLayout(editor_widget)
        editor_layout.setContentsMargins(0, 0, 0, 0)

         # 键盘部件
        self.keyboard = LatexKeyboard()
        self.keyboard.insertSymbol.connect(self.insert_text)
        
        self.editor = QPlainTextEdit()
        self.editor.setStyleSheet("""
            QPlainTextEdit {{
                font-family: Consolas;
                font-size: 14px;
                padding: 15px;
            }}
        """)
        editor_layout.addWidget(self.editor)
        editor_layout.addWidget(self.keyboard)

        self.splitter.addWidget(editor_widget)

        # 右侧预览区域
        self.preview = QWebEngineView()
        self.splitter.addWidget(self.preview)

        # 设置初始比例
        self.splitter.setSizes([200, 600, 600])

        # 初始化定时器
        self.render_timer = QTimer()
        self.render_timer.setSingleShot(True)
        self.render_timer.timeout.connect(self.update_all)
        self.editor.textChanged.connect(self.schedule_update)

        # 新增：初始化菜单栏
        self.init_menu()


    def insert_text(self, symbol):
        cursor = self.editor.textCursor()
        cursor.insertText(symbol)
        # cursor.insertText(rf"{symbol}")  # 使用 raw string



    def init_menu(self):
        """初始化菜单系统"""
        # 文件菜单
        file_menu = self.menuBar().addMenu("文件")

        # 新建
        new_action = file_menu.addAction("新建")
        new_action.setShortcut("Ctrl+N")
        new_action.triggered.connect(self.new_file)

        # 打开
        open_action = file_menu.addAction("打开")
        open_action.setShortcut("Ctrl+O")
        open_action.triggered.connect(self.open_file)

        # 保存
        save_action = file_menu.addAction("保存")
        save_action.setShortcut("Ctrl+S")
        save_action.triggered.connect(self.save_file)

        # 另存为
        save_as_action = file_menu.addAction("另存为")
        save_as_action.triggered.connect(self.save_as_file)

        # 导出PDF
        export_pdf_action = file_menu.addAction("导出PDF")
        export_pdf_action.setShortcut("Ctrl+P")
        export_pdf_action.triggered.connect(self.export_pdf)

        # 分隔符
        file_menu.addSeparator()

        # 退出
        exit_action = file_menu.addAction("退出")
        exit_action.setShortcut("Ctrl+Q")
        exit_action.triggered.connect(self.close)


    def new_file(self):
        """新建文件"""
        self.editor.clear()
        self.current_file = "Untitled.md"

    def open_file(self):
        """打开文件"""
        path, _ = QFileDialog.getOpenFileName(
            self, "打开文件", "", "Markdown文件 (*.md);;所有文件 (*)")

        if path:
            try:
                with open(path, 'r', encoding='utf-8') as f:
                    self.editor.setPlainText(f.read())
                    self.current_file = path
            except Exception as e:
                QMessageBox.critical(self, "错误", f"无法打开文件:\n{str(e)}")

    def save_file(self):
        """保存文件"""
        if self.current_file:
            try:
                with open(self.current_file, 'w', encoding='utf-8') as f:
                    f.write(self.editor.toPlainText())
            except Exception as e:
                QMessageBox.critical(self, "错误", f"保存失败:\n{str(e)}")
        else:
            self.save_as_file()

    def save_as_file(self):
        """另存为文件"""
        path, _ = QFileDialog.getSaveFileName(
            self, "另存为", "", "Markdown文件 (*.md);;所有文件 (*)")

        if path:
            try:
                with open(path, 'w', encoding='utf-8') as f:
                    f.write(self.editor.toPlainText())
                    self.current_file = path
            except Exception as e:
                QMessageBox.critical(self, "错误", f"保存失败:\n{str(e)}")

    def export_pdf(self):
        """导出为PDF"""
        path, _ = QFileDialog.getSaveFileName(
            self, "导出PDF", "", "PDF文件 (*.pdf)")

        if path:
            # 确保文件扩展名
            if not path.lower().endswith('.pdf'):
                path += '.pdf'

            # 创建打印机对象
            self.printer = QPrinter()
            self.printer.setOutputFormat(QPrinter.PdfFormat)
            self.printer.setOutputFileName(path)
            # printer.setPageMargins(Qt.QMarginsF(15, 15, 15, 15), QPrinter.Millimeter)

            # 创建进度对话框（关键修复2）
            # self.progress_dialog = QProgressDialog(
            #     "正在生成PDF...", "取消", 0, 0, self)
            # self.progress_dialog.setWindowModality(Qt.WindowModal)
            # self.progress_dialog.show()

            # 使用直接打印方式（关键修复3）
            self.preview.page().printToPdf(self.printer.outputFileName())

    def pdf_export_callback(self, pdf_data):
        """PDF导出回调（线程安全版本）"""
        try:
            # 关闭进度对话框
            self.progress_dialog.close()

            if pdf_data:
                # 保存PDF文件
                with open(self.printer.outputFileName(), 'wb') as f:
                    f.write(pdf_data)
                QMessageBox.information(self, "成功", "PDF导出完成！")
            else:
                QMessageBox.critical(self, "错误", "PDF生成失败！")
                
        except Exception as e:
            QMessageBox.critical(self, "错误", f"保存失败：{str(e)}")
            
        finally:
            # 显式释放资源（关键修复4）
            del self.printer
            self.printer = None

    def preview_test(self):
        """初始化默认内容"""
        sample_text = r"""# 一级标题
## 二级标题1
### 三级标题
## 二级标题2

$$
\begin{bmatrix}
a & b \\
c & d 
\end{bmatrix}
$$
"""
        self.editor.setPlainText(sample_text)
    def init_content(self):

        self.preview_test()

        self.update_all()

    def schedule_update(self):
        """调度更新"""
        self.render_timer.start(800)

    def update_all(self):
        """更新大纲和预览"""
        self.update_outline()
        self.update_preview()

    def update_outline(self):
        """解析并更新大纲树"""
        text = self.editor.toPlainText()
        headings = self.parse_headings(text)
        
        self.outline_tree.clear()
        root_node = self.outline_tree.invisibleRootItem()
        stack = [root_node]
        current_level = 0

        for level, title, line_num in headings:
            item = QTreeWidgetItem([title])
            item.line_num = line_num
            
            while level <= current_level and len(stack) > 1:
                stack.pop()
                current_level -= 1

            stack[-1].addChild(item)
            
            if level > current_level:
                stack.append(item)
                current_level = level

        self.outline_tree.expandAll()

    def parse_headings(self, text):
        """解析标题层次结构"""
        headings = []
        lines = text.split('\n')
        heading_re = re.compile(r'^(#+)\s+(.+)$')
        
        for line_num, line in enumerate(lines):
            match = heading_re.match(line.strip())
            if match:
                level = len(match.group(1))
                title = match.group(2).strip()
                headings.append((level, title, line_num))
        
        return headings

    def jump_to_heading(self, item):
        """跳转到对应标题位置"""
        cursor = self.editor.textCursor()
        doc = self.editor.document()
        
        # 定位到对应行
        line_num = getattr(item, 'line_num', 0)
        block = doc.findBlockByLineNumber(line_num)
        
        cursor.setPosition(block.position())
        self.editor.setTextCursor(cursor)
        self.editor.setFocus()

    def update_preview(self):
        """更新预览内容"""
        md_text = self.editor.toPlainText()


        # protected_text = md_text.replace("\\", "\\\\")  # 转成双反斜杠
        # protected_text = protected_text.replace("\\\\", r"\\")  # 转义为保留状态

        md = markdown.Markdown(
            extensions=[
                'markdown.extensions.extra',
                'markdown.extensions.codehilite',
                'mdx_math'  
            ],
            extension_configs= {
                'mdx_math': {
                    'add_preview': True,
                    'enable_dollar_delimiter': True,
                },
            }
        )

        html = md.convert(md_text)

        full_html = MathFormat.format(html)

        print(full_html)

        self.preview.setHtml(full_html, baseUrl=self.baseurl)

if __name__ == "__main__":
    QApplication.setAttribute(Qt.AA_EnableHighDpiScaling)
    app = QApplication(sys.argv)
    window = MarkdownEditor()
    window.show()
    sys.exit(app.exec_())